/*
Copyright 2008, Mark Arvidson

This file is part of EwaScorecard.

EwaScorecard is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

EwaScorecard is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with EwaScorecard.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.io.*;
import java.util.*;

public class TeamData {
    // persisted fields

    private String name;
    private Vector gymnasts = new Vector();
    // transient fields
    private int numberOfEvents;
    private float teamScore;
    // keep track of first, second and third placed gymnasts in each event
    private int[] firstScores;
    private int[] secondScores;
    private int[] thirdScores;

    public TeamData(String name, int numberOfEvents) {
        this.name = name;
        this.numberOfEvents = numberOfEvents;
        clearTopArrays();
    }

    /**
     * @return the Team name
     */
    public String getName() {
        return name;
    }

    /**
     * Get the team score
     * @return
     */
    public float getTeamScore() {
        return teamScore;
    }

    /** 
     * Get an array containing all the gymnasts' names.
     * @return String array of gymnast names
     */
    public String[] getGymnasts() {
        String[] result = new String[gymnasts.size()];
        Enumeration namesEnum = gymnasts.elements();
        for (int i = 0; i < result.length && namesEnum.hasMoreElements(); i++) {
            result[i] = namesEnum.nextElement().toString();
        }
        return result;
    }

    /**
     * The vector of gymnasts.  This should probably be private.
     * @return the collection of gymnasts
     */
    public Vector gymnasts() {
        return gymnasts;
    }

    public TeamData clone() {
        TeamData newTeam = new TeamData(this.name, this.getNumberOfEvents());

        Enumeration gymnastEnum = gymnasts.elements();
        while (gymnastEnum.hasMoreElements()) {
            GymnastData gymnast = (GymnastData) gymnastEnum.nextElement();
            newTeam.addGymnast(gymnast.getNumber(), gymnast.getName(), null);
        }
        return newTeam;
    }

    public int rowCount() {
        return gymnasts.size();
    }

    public GymnastData getGymnast(int rowIndex) {
        return (GymnastData) gymnasts.elementAt(rowIndex);
    }

    public float rowData(int rowIndex, int columnIndex) {
        if (rowIndex > gymnasts.size()) {
            throw new ArrayIndexOutOfBoundsException("ArrayIndex out of bounds: expected 0.." +
                    gymnasts.size() + ", got " + rowIndex);
        }
        if (columnIndex > getNumberOfEvents() + 1) {
            throw new ArrayIndexOutOfBoundsException("ArrayIndex out of bounds: expected 0.." +
                    (getNumberOfEvents() + 1) + ", got " + columnIndex);
        }
        return ((GymnastData) gymnasts.elementAt(rowIndex)).getScores()[columnIndex];
    }

    /* Returns the relative score placement of the given row.  
     * Returns 1st, 2nd, 3rd, or -1 otherwise */
    public int getScorePlacement(int rowIndex, int columnIndex) {
        if (firstScores[columnIndex] == rowIndex) {
            return 1;
        } else if (secondScores[columnIndex] == rowIndex) {
            return 2;
        } else if (thirdScores[columnIndex] == rowIndex) {
            return 3;
        }
        return -1;
    }

    public GymnastData addGymnast(int gymnastNumber, String name, 
            float[] scores) {
        // look for duplicates and overwrite
        GymnastData newData = new GymnastData(gymnastNumber, name, createScoreArray());
        gymnasts.removeElement(newData);
        
        // alphabetize by team and name
        int index = -1;
        Enumeration elemEnum = gymnasts.elements();
        while (index == -1 && elemEnum.hasMoreElements()) {
            GymnastData oldData = (GymnastData) elemEnum.nextElement();
            int nameCompare = newData.getName().compareTo(oldData.getName());
            if (nameCompare < 0) {
                index = gymnasts.indexOf(oldData);
            }
        }
        if (index == -1) {
            gymnasts.addElement(newData);
        } else {
            gymnasts.insertElementAt(newData, index);
        }
        if (scores != null) {
            for (int score = 0; score < scores.length; score++) {
                newData.setScore(score, scores[score]);
            }
        }
        updateTops();
        return newData;
    }

    private void clearTopArrays() {
        // save room for dessert (AA)
        this.firstScores = new int[numberOfEvents + 1];
        this.secondScores = new int[numberOfEvents + 1];
        this.thirdScores = new int[numberOfEvents + 1];
        for (int i = 0; i < numberOfEvents; i++) {
            this.firstScores[i] = -1;
            this.secondScores[i] = -1;
            this.thirdScores[i] = -1;
        }
        this.firstScores[numberOfEvents] = -1;
        this.secondScores[numberOfEvents] = -1;
        this.thirdScores[numberOfEvents] = -1;
    }

    private float[] createScoreArray() {
        float[] scores = new float[getNumberOfEvents() + 1];
        for (int index = 0; index < scores.length; index++) {
            scores[index] = 0;
        }
        return scores;
    }
    
    public void deleteGymnast(GymnastData toDelete) {
        gymnasts.removeElement(toDelete);
        updateTops();
    }

    public void setScore(int gymnastIndex, int event, float score) {
        ((GymnastData) gymnasts.elementAt(gymnastIndex)).setScore(event, score);
        updateTops();
    }

    public String toString() {
        return getName();
    }

    /* Update top scores and AA */
    private void updateTops() {
        clearTopArrays();
        float[] first = createScoreArray();
        float[] second = createScoreArray();
        float[] third = createScoreArray();
        int gymnastIndex = 0;
        Enumeration elemEnum = gymnasts.elements();
        while (elemEnum.hasMoreElements()) {
            GymnastData gymnast = (GymnastData) elemEnum.nextElement();
            for (int index = 0; index < getNumberOfEvents(); index++) {
                float score = gymnast.getScores()[index];
                if (score > first[index]) {
                    third[index] = second[index];
                    thirdScores[index] = secondScores[index];
                    second[index] = first[index];
                    secondScores[index] = firstScores[index];
                    first[index] = score;
                    firstScores[index] = gymnastIndex;
                } else if (score > second[index]) {
                    third[index] = second[index];
                    thirdScores[index] = secondScores[index];
                    second[index] = score;
                    secondScores[index] = gymnastIndex;
                } else if (score > third[index]) {
                    third[index] = score;
                    thirdScores[index] = gymnastIndex;
                }
            }

            float allAround = 0;
            for (int index = 0; index < getNumberOfEvents(); index++) {
                allAround += gymnast.getScores()[index];
            }
            gymnast.setScore(numberOfEvents, allAround);

            gymnastIndex++;
        }

        teamScore = 0;
        for (int index = 0; index < getNumberOfEvents(); index++) {
            teamScore += first[index] + second[index] + third[index];
        }
    }

    public void storeRecord(DataOutputStream stream, boolean storeAsDefault) {
        try {
            stream.writeUTF(name);
            stream.writeInt(getNumberOfEvents());
            stream.writeInt(gymnasts.size());
            Enumeration gymnastEnum = gymnasts.elements();
            while (gymnastEnum.hasMoreElements()) {
                GymnastData data = (GymnastData) gymnastEnum.nextElement();
                data.storeRecord(stream, storeAsDefault);
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }

    public static TeamData restoreRecord(DataInputStream stream) {
        try {
            String name = stream.readUTF();
            int number = stream.readInt();
            int length = stream.readInt();
            TeamData result = new TeamData(name, number);
            for (int i = 0; i < length; i++) {
                result.gymnasts.addElement(GymnastData.restoreRecord(stream));
            }
            result.updateTops();
            return result;
        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    public int getNumberOfEvents() {
        return numberOfEvents;
    }
}
